/*BEGIN_LEGAL 
Intel Open Source License 

Copyright (c) 2002-2011 Intel Corporation. All rights reserved.
 
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.  Redistributions
in binary form must reproduce the above copyright notice, this list of
conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.  Neither the name of
the Intel Corporation nor the names of its contributors may be used to
endorse or promote products derived from this software without
specific prior written permission.
 
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR
ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
END_LEGAL */
#define _GNU_SOURCE
#include <ucontext.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/utsname.h>
#include "raise-exception-addrs.h"
#include "faultcheck-target.h"

#define PAGESIZE 4096


extern void DoUnmappedWrite(void *);
extern void DoUnmappedRead(void *);
extern void DoInaccessibleRead(void *);
extern void DoInaccessibleWrite(void *);
extern void DoUD2();
extern void DoPrivilegedInstruction();
extern void DoIntegerDivideByZero();
extern void DoIntegerOverflowTrap();
extern void DoX87DivideByZero();
extern void DoX87Overflow();
extern void DoX87Underflow();
extern void DoX87Precision();
extern void DoX87InvalidOperation();
extern void DoX87DenormalizedOperand();
extern void DoX87StackUnderflow();
extern void DoX87StackOverflow();
extern void DoX87MultipleExceptions();
extern void DoSIMDDivideByZero();
extern void DoSIMDOverflow();
extern void DoSIMDUnderflow();
extern void DoSIMDPrecision();
extern void DoSIMDInvalidOperation();
extern void DoSIMDDenormalizedOperand();
extern void DoSIMDMultipleExceptions();
extern void DoBreakpointTrap();
extern void DoSingleStepTrap();
extern void DoBadRegister();
extern void ClearAC();
extern void DoIllegalSetOfSegReg1();
extern void DoIllegalSetOfSegReg2();
extern void DoIllegalSetOfSegReg3();
extern void DoIllegalGetOfSegReg1();
extern void DoIllegalGetOfSegReg2();

extern char PinLab_UnmappedRead;
extern char PinLab_UnmappedWrite;
extern char PinLab_InaccessibleRead;
extern char PinLab_InaccessibleWrite;
extern char PinLab_MisalignedRead;
extern char PinLab_MisalignedWrite;
extern char PinLab_IllegalInstruction;
extern char PinLab_PrivilegedInstruction;
extern char PinLab_IntegerDivideByZero;
extern char PinLab_IntegerOverflowTrap;
extern char PinLab_X87DivideByZero;
extern char PinLab_X87Overflow;
extern char PinLab_X87Underflow;
extern char PinLab_X87Precision;
extern char PinLab_X87InvalidOperation;
extern char PinLab_X87DenormalizedOperand;
extern char PinLab_X87StackUnderflow;
extern char PinLab_X87StackOverflow;
extern char PinLab_X87MultipleExceptions;
extern char PinLab_SIMDDivideByZero;
extern char PinLab_SIMDOverflow;
extern char PinLab_SIMDUnderflow;
extern char PinLab_SIMDPrecision;
extern char PinLab_SIMDInvalidOperation;
extern char PinLab_SIMDDenormalizedOperand;
extern char PinLab_SIMDMultipleExceptions;
extern char PinLab_BreakpointTrap;

#if defined(TARGET_IA32)
extern void DoBoundTrap();
extern char PinLab_BoundTrap;
#endif


#define BYTES_PER_TRAP_FUNC     3       /* number bytes in each "IntTrap" function */
#define NUM_TRAP_FUNCS          256     /* number of "IntTrap" functions */

static char NoPermBuffer[2*PAGESIZE];
static void *PageNoPerm = 0;
static char MisalignedBuffer[16];
static void *MisalignedAddress = 0;
static void *UnmappedAddress = (void *)8;
static unsigned char IntTrapBuffer[3*PAGESIZE];
static unsigned char *IntTrapCode;

static int IsBadKernel;
static int PrintSiAddr;
static int PrintMxcsr;
static int PrintX87Status;



/*
 * Do one-time initializations for the tests.  Returns 1 on success, 0 on failure.
 */
int Initialize()
{
    RAISE_EXCEPTION_ADDRS pinAddrs;
    struct utsname uinfo;
    unsigned long addr;
    int trapNo;
    int i;


    /*
     * Create a page without read or write permission.
     */
    if (getpagesize() != PAGESIZE)
    {
        fprintf(stderr, "Wrong page size\n");
        return 0;
    }
    addr = (unsigned long)&NoPermBuffer[0];
    addr = (addr + PAGESIZE) & ~(PAGESIZE-1);
    PageNoPerm = (void *)addr;
    if (mprotect(PageNoPerm, PAGESIZE, 0) != 0)
    {
        fprintf(stderr, "mprotect failed\n");
        return 0;
    }

    /*
     * Get a misaligned pointer to a 8-byte buffer.
     */
    addr = (unsigned long)&MisalignedBuffer[0];
    MisalignedAddress = (void *)(((addr + 8) & ~7) + 1);

    /*
     * Create code for all possible 'INT x' instructions.  We construct the code
     * so that it falls at the end of a page and the next page is not accessible.
     * This helps test SMC.  See Mantis #1795.
     */
    addr = (unsigned long)&IntTrapBuffer[0];
    addr = (addr + PAGESIZE) & ~(PAGESIZE-1);
    if (mprotect((void *)addr, PAGESIZE, (PROT_READ|PROT_WRITE|PROT_EXEC)) != 0)
    {
        fprintf(stderr, "mprotect failed\n");
        return 0;
    }
    if (mprotect((void *)(addr+PAGESIZE), PAGESIZE, 0) != 0)
    {
        fprintf(stderr, "mprotect failed\n");
        return 0;
    }
    IntTrapCode = (void *)(addr + PAGESIZE - BYTES_PER_TRAP_FUNC * NUM_TRAP_FUNCS);

    for (i = 0, trapNo = 0;  trapNo < NUM_TRAP_FUNCS;  trapNo++)
    {
        IntTrapCode[i++] = 0xcd;    /* int <trapNo> */
        IntTrapCode[i++] = trapNo;
        IntTrapCode[i++] = 0xc3;    /* ret */
    }
    assert(i == BYTES_PER_TRAP_FUNC * NUM_TRAP_FUNCS);

    /*
     * Tell the Pin tool about the labels for some of the instructions that raise
     * exceptions.  The tool may instrument these instructions.
     */
    memset(&pinAddrs, 0, sizeof(pinAddrs));
    pinAddrs._unmappedRead = &PinLab_UnmappedRead;
    pinAddrs._unmappedReadAddr = UnmappedAddress;
    pinAddrs._unmappedWrite = &PinLab_UnmappedWrite;
    pinAddrs._unmappedWriteAddr = UnmappedAddress;
    pinAddrs._inaccessibleRead = &PinLab_InaccessibleRead;
    pinAddrs._inaccessibleReadAddr = PageNoPerm;
    pinAddrs._inaccessibleWrite = &PinLab_InaccessibleWrite;
    pinAddrs._inaccessibleWriteAddr = PageNoPerm;
    pinAddrs._misalignedRead = &PinLab_MisalignedRead;
    pinAddrs._misalignedWrite = &PinLab_MisalignedWrite;
    pinAddrs._illegalInstruction = &PinLab_IllegalInstruction;
    pinAddrs._privilegedInstruction = &PinLab_PrivilegedInstruction;
    pinAddrs._integerDivideByZero = &PinLab_IntegerDivideByZero;
    pinAddrs._integerOverflowTrap = &PinLab_IntegerOverflowTrap;
#if defined(TARGET_IA32)
    pinAddrs._boundTrap = &PinLab_BoundTrap;
#endif
    pinAddrs._x87DivideByZero = &PinLab_X87DivideByZero;
    pinAddrs._x87Overflow = &PinLab_X87Overflow;
    pinAddrs._x87Underflow = &PinLab_X87Underflow;
    pinAddrs._x87Precision = &PinLab_X87Precision;
    pinAddrs._x87InvalidOperation = &PinLab_X87InvalidOperation;
    pinAddrs._x87DenormalizedOperand = &PinLab_X87DenormalizedOperand;
    pinAddrs._x87StackUnderflow = &PinLab_X87StackUnderflow;
    pinAddrs._x87StackOverflow = &PinLab_X87StackOverflow;
    pinAddrs._x87MultipleExceptions = &PinLab_X87MultipleExceptions;
    pinAddrs._simdDivideByZero = &PinLab_SIMDDivideByZero;
    pinAddrs._simdOverflow = &PinLab_SIMDOverflow;
    pinAddrs._simdUnderflow = &PinLab_SIMDUnderflow;
    pinAddrs._simdPrecision = &PinLab_SIMDPrecision;
    pinAddrs._simdInvalidOperation = &PinLab_SIMDInvalidOperation;
    pinAddrs._simdDenormalizedOperand = &PinLab_SIMDDenormalizedOperand;
    pinAddrs._simdMultipleExceptions = &PinLab_SIMDMultipleExceptions;
    pinAddrs._breakpointTrap = &PinLab_BreakpointTrap;
    SetLabelsForPinTool(&pinAddrs);

    /*
     * Some RHEL3 kernels are known to be broken in a way that causes this
     * test to fail.  Kernels "2.4.21-17" (Taroon Update 3) are known broken,
     * while kernels "2.4.21-37" (Taroon Update 6) are known to be OK.  If we
     * discover other broken kernel versions, the test below should be updated.
     *
     * Broken kernels only appear to be broken when running an IA32 application
     * on an Intel64 kernel.  In the broken case, the kernel appears to leave
     * the "si_code" field uninitialized in the siginfo_t structure.  This confuses
     * Pin (resulting in a Pin crash) for some of the tests below.  Other tests
     * fail simply because Pin emulates the correct si_code value.  However the test
     * fails because, we compare the Pin emulated si_code value against the native
     * si_code value.
     */
    IsBadKernel = 0;
#if defined(TARGET_IA32)
    if (uname(&uinfo) == 0)
    {
        if ((strncmp(uinfo.machine, "x86_64", sizeof("x86_64")-1) == 0) &&
            (strncmp(uinfo.release, "2.4.21-17", sizeof("2.4.21-17")-1) == 0))
        {
            IsBadKernel = 1;
        }
    }
#endif

    return 1;
}


TSTATUS DoTest(unsigned int tnum)
{
    unsigned int trapNo;
    void (*fn)();

    PrintSiAddr = 0;
    PrintMxcsr = 0;
    PrintX87Status = 0;

    switch (tnum)
    {
    case 0:
        printf("  Read from unmapped address (EXCEPTCODE_ACCESS_INVALID_ADDRESS)\n");
        PrintSiAddr = 1;
        DoUnmappedRead(UnmappedAddress);
        return TSTATUS_NOFAULT;
    case 1:
        printf("  Write to unmapped address (EXCEPTCODE_ACCESS_INVALID_ADDRESS)\n");
        PrintSiAddr = 1;
        DoUnmappedWrite(UnmappedAddress);
        return TSTATUS_NOFAULT;
    case 2:
        printf("  Jump to unmapped address (EXCEPTCODE_ACCESS_INVALID_ADDRESS)\n");
#if !defined(TARGET_BSD)
        if (!IsBadKernel)
        {
            PrintSiAddr = 1;
            fn = UnmappedAddress;
            fn();
            return TSTATUS_NOFAULT;
        }
        else
        {
            return TSTATUS_SKIP;
        }
#else
        /* BSD does not support jumping to invalid code yet.  See Mantis #1940 */
        return TSTATUS_SKIP;
#endif
    case 3:
        printf("  Read from inaccessible address (EXCEPTCODE_ACCESS_DENIED)\n");
        PrintSiAddr = 1;
        DoInaccessibleRead(PageNoPerm);
        return TSTATUS_NOFAULT;
    case 4:
        printf("  Write to inaccessible address (EXCEPTCODE_ACCESS_DENIED)\n");
        PrintSiAddr = 1;
        DoInaccessibleWrite(PageNoPerm);
        return TSTATUS_NOFAULT;
    case 5:
        printf("  Jump to inaccessible address (EXCEPTCODE_ACCESS_DENIED)\n");
#if !defined(TARGET_BSD)
        if (!IsBadKernel)
        {
            PrintSiAddr = 1;
            fn = PageNoPerm;
            fn();
            return TSTATUS_NOFAULT;
        }
        else
        {
            return TSTATUS_SKIP;
        }
#else
        /* BSD does not support jumping to invalid code yet.  See Mantis #1940 */
        return TSTATUS_SKIP;
#endif
    case 6:
        printf("  Read from misaligned address (EXCEPTCODE_ACCESS_MISALIGNED)\n");
#if !defined(TARGET_BSD)
        DoMisalignedRead(MisalignedAddress);
        return TSTATUS_NOFAULT;
#else
        /* This causes problems on BSD.  See Mantis #1940 */
        return TSTATUS_SKIP;
#endif
    case 7:
        printf("  Write to misaligned address (EXCEPTCODE_ACCESS_MISALIGNED)\n");
#if !defined(TARGET_BSD)
        DoMisalignedWrite(MisalignedAddress);
        return TSTATUS_NOFAULT;
#else
        /* This causes problems on BSD.  See Mantis #1940 */
        return TSTATUS_SKIP;
#endif
    case 8:
        printf("  Execute illegal instruction (EXCEPTCODE_ILLEGAL_INS)\n");
        PrintSiAddr = 1;
        DoUD2();
        return TSTATUS_NOFAULT;
    case 9:
        printf("  Execute privileged instruction (EXCEPTCODE_PRIVILEGED_INS)\n");
        DoPrivilegedInstruction();
        return TSTATUS_NOFAULT;
    case 10:
        printf("  Integer divide by zero (EXCEPTCODE_INT_DIVIDE_BY_ZERO)\n");
        PrintSiAddr = 1;
        DoIntegerDivideByZero();
        return TSTATUS_NOFAULT;
    case 11:
        printf("  Integer overflow trap (EXCEPTCODE_INT_OVERFLOW_TRAP)\n");
        DoIntegerOverflowTrap();
        return TSTATUS_NOFAULT;
    case 12:
        printf("  Array index bound trap (EXCEPTCODE_INT_BOUNDS_EXCEEDED)\n");
#if defined(TARGET_IA32)
        /*
         * The BOUND instruction only exists on IA32.
         */
        DoBoundTrap();
        return TSTATUS_NOFAULT;
#else
        return TSTATUS_SKIP;
#endif
    case 13:
        printf("  X87 divide by zero (EXCEPTCODE_X87_DIVIDE_BY_ZERO)\n");
        PrintSiAddr = 1;
        PrintX87Status = 1;
        DoX87DivideByZero();
        return TSTATUS_NOFAULT;
    case 14:
        printf("  X87 overflow (EXCEPTCODE_X87_OVERFLOW)\n");
        PrintSiAddr = 1;
        PrintX87Status = 1;
        DoX87Overflow();
        return TSTATUS_NOFAULT;
    case 15:
        printf("  X87 underflow (EXCEPTCODE_X87_UNDERFLOW)\n");
        PrintSiAddr = 1;
        PrintX87Status = 1;
        DoX87Underflow();
        return TSTATUS_NOFAULT;
    case 16:
        printf("  X87 precision (EXCEPTCODE_X87_INEXACT_RESULT)\n");
        PrintSiAddr = 1;
        PrintX87Status = 1;
        DoX87Precision();
        return TSTATUS_NOFAULT;
    case 17:
        printf("  X87 invalid operation (EXCEPTCODE_X87_INVALID_OPERATION)\n");
        PrintSiAddr = 1;
        PrintX87Status = 1;
        DoX87InvalidOperation();
        return 1;
    case 18:
        printf("  X87 denormalized operand (EXCEPTCODE_X87_DENORMAL_OPERAND)\n");
        PrintSiAddr = 1;
        PrintX87Status = 1;
        DoX87DenormalizedOperand();
        return TSTATUS_NOFAULT;
    case 19:
        printf("  X87 stack underflow (EXCEPTCODE_X87_STACK_ERROR)\n");
        PrintSiAddr = 1;
        PrintX87Status = 1;
        DoX87StackUnderflow();
        return TSTATUS_NOFAULT;
    case 20:
        printf("  X87 stack overflow (EXCEPTCODE_X87_STACK_ERROR)\n");
        PrintSiAddr = 1;
        PrintX87Status = 1;
        DoX87StackOverflow();
        return 1;
    case 21:
        printf("  X87 multiple exceptions\n");
        PrintSiAddr = 1;
        PrintX87Status = 1;
        DoX87MultipleExceptions();
        return 1;
    case 22:
        printf("  SIMD divide by zero (EXCEPTCODE_SIMD_DIVIDE_BY_ZERO)\n");
        PrintSiAddr = 1;
        PrintMxcsr = 1;
        DoSIMDDivideByZero();
        return TSTATUS_NOFAULT;
    case 23:
        printf("  SIMD overflow (EXCEPTCODE_SIMD_OVERFLOW)\n");
        PrintSiAddr = 1;
        PrintMxcsr = 1;
        DoSIMDOverflow();
        return TSTATUS_NOFAULT;
    case 24:
        printf("  SIMD underflow (EXCEPTCODE_SIMD_UNDERFLOW)\n");
        PrintSiAddr = 1;
        PrintMxcsr = 1;
        DoSIMDUnderflow();
        return TSTATUS_NOFAULT;
    case 25:
        printf("  SIMD precision (EXCEPTCODE_SIMD_INEXACT_RESULT)\n");
        PrintSiAddr = 1;
        PrintMxcsr = 1;
        DoSIMDPrecision();
        return TSTATUS_NOFAULT;
    case 26:
        printf("  SIMD invalid operation (EXCEPTCODE_SIMD_INVALID_OPERATION)\n");
        PrintSiAddr = 1;
        PrintMxcsr = 1;
        DoSIMDInvalidOperation();
        return TSTATUS_NOFAULT;
    case 27:
        printf("  SIMD denormalized operand (EXCEPTCODE_SIMD_DENORMAL_OPERAND)\n");
        PrintSiAddr = 1;
        PrintMxcsr = 1;
        DoSIMDDenormalizedOperand();
        return TSTATUS_NOFAULT;
    case 28:
        printf("  SIMD multiple exceptions\n");
        PrintSiAddr = 1;
        PrintMxcsr = 1;
        DoSIMDMultipleExceptions();
        return TSTATUS_NOFAULT;
    case 29:
        printf("  breakpoint trap (EXCEPTCODE_DBG_BREAKPOINT_TRAP)\n");
        DoBreakpointTrap();
        return TSTATUS_NOFAULT;
    case 30:
        printf("  single-step trap (EXCEPTCODE_DBG_SINGLE_STEP_TRAP)\n");
#if 0
        /*
         * Pin doesn't handle this well now, so it is disabled.
         */
        PrintSiAddr = 1;
        DoSingleStepTrap();
        return TSTATUS_NOFAULT;
#else
        return TSTATUS_SKIP;
#endif
    case 31:
        printf("  bad register encoding\n");
#if !defined(TARGET_BSD)
        PrintSiAddr = 1;
        DoBadRegister();
        return TSTATUS_NOFAULT;
#else
        /* This causes problems on BSD.  See Mantis #1940 */
        return TSTATUS_SKIP;
#endif
    case 32:
        printf("  put illegal value into segment register gs/fs\n");
#if !defined(TARGET_BSD)
        PrintSiAddr = 1;
        DoIllegalSetOfSegReg1();
        return TSTATUS_NOFAULT;
#else
        /* This causes problems on BSD.  See Mantis #1940 */
        return TSTATUS_SKIP;
#endif
    case 33:
        printf("  illegal mem access in lfs/lgs \n");
#if !defined(TARGET_BSD)
        PrintSiAddr = 0;
        DoIllegalSetOfSegReg2();
        return TSTATUS_NOFAULT;
#else
        /* This causes problems on BSD.  See Mantis #1940 */
        return TSTATUS_SKIP;
#endif
    case 34:
        printf("  illegal mem access in RW to segment register\n");
#if !defined(TARGET_BSD)
        PrintSiAddr = 1;
        DoIllegalGetOfSegReg1();
        return TSTATUS_NOFAULT;
#else
        /* This causes problems on BSD.  See Mantis #1940 */
        return TSTATUS_SKIP;
#endif
    default:
        trapNo = tnum - 34;
        if (trapNo >= NUM_TRAP_FUNCS)
            break;

        printf("  INT %u (at %p)\n", trapNo, (void *)&IntTrapCode[trapNo*BYTES_PER_TRAP_FUNC]);

        /*
         * Skip 'int 3' because some Linux kernels don't handle it well.  See Mantis #666.
         * Skip 'int 0x80' because it is a system call trap on Linux and BSD.
         */
        if (trapNo == 3 || trapNo == 0x80)
            return TSTATUS_SKIP;

        PrintSiAddr = 1;
        fn = (void (*)(void))&IntTrapCode[trapNo*BYTES_PER_TRAP_FUNC];
        fn();
        return TSTATUS_NOFAULT;
    }

    return TSTATUS_DONE;
}


void PrintSignalContext(int sig, const siginfo_t *info, void *vctxt)
{
    ucontext_t *ctxt = vctxt;
    unsigned long rip;
    long int trapno;
    long int mxcsr = 0;
    long int x87sw = 0;

    /*
     * Some of the tests set the AC bit.  Clear it to prevent alignment-check faults
     * while doing the print below.
     */
    ClearAC();

#if defined(TARGET_BSD)
    rip = (unsigned long)ctxt->uc_mcontext.mc_rip;
    trapno = (long int)ctxt->uc_mcontext.mc_trapno;
#elif defined(TARGET_LINUX) && defined(TARGET_IA32E)
    rip = (unsigned long)ctxt->uc_mcontext.gregs[REG_RIP];
    trapno = (long int)ctxt->uc_mcontext.gregs[REG_TRAPNO];
    if (ctxt->uc_mcontext.fpregs)
    {
        mxcsr = (long int)ctxt->uc_mcontext.fpregs->mxcsr;
        x87sw = (long int)ctxt->uc_mcontext.fpregs->swd;
    }
#elif defined(TARGET_LINUX) && defined(TARGET_IA32)
    rip = (unsigned long)ctxt->uc_mcontext.gregs[REG_EIP];
    trapno = (long int)ctxt->uc_mcontext.gregs[REG_TRAPNO];
    if (ctxt->uc_mcontext.fpregs)
    {
        x87sw = (long int)ctxt->uc_mcontext.fpregs->sw;
    }
#endif

    printf("  Signal %d, pc=0x%lx, si_errno=%d, trap_no=%ld",
        sig,
        rip,
        (int)info->si_errno,
        trapno);

    if (!IsBadKernel)
        printf(", si_code=%d", (int)info->si_code);
    if (PrintSiAddr)
        printf(", si_addr=0x%lx", (unsigned long)info->si_addr);
    if (PrintMxcsr)
        printf(", mxcsr=0x%lx", mxcsr);
    if (PrintX87Status)
        printf(", x87sw=0x%lx", x87sw);
    printf("\n");
}
